package com.nuan.shiro.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.crypto.MacProvider;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.util.Date;


public class JwtUtils
{

    /**
     * JWT 加解密类型
     */
    private static final SignatureAlgorithm JWT_ALG = SignatureAlgorithm.HS256;
    /**
     * JWT 生成密钥使用的密码
     */
    private static final String JWT_RULE = "zhonglvzhineng";

    /**
     * JWT 添加至HTTP HEAD中的前缀
     */
    private static final String JWT_SEPARATOR = "zl";

    /**
     * 密钥：通过生成 JWT_ALG 和 JWT_RULE 加密生成
     */
    private static final Key SECRET = generateKey(JWT_ALG, JWT_RULE);

    /**
     * 默认jwt所面向的分组用户
     */
    private static final String DEFAULT_SUB = "system";

    /**
     * 使用JWT默认方式，生成加解密密钥
     *
     * @param alg 加解密类型
     * @return
     */
    private static SecretKey generateKey(SignatureAlgorithm alg) {
        return MacProvider.generateKey(alg);
    }

    /**
     * 使用指定密钥生成规则，生成JWT加解密密钥
     *
     * @param alg  加解密类型
     * @param rule 密钥生成规则
     * @return
     */
    private static SecretKey generateKey(SignatureAlgorithm alg, String rule) {
        // 将密钥生成键转换为字节数组
        byte[] bytes = Base64.decodeBase64(rule);
        // 根据指定的加密方式，生成密钥
        return new SecretKeySpec(bytes, alg.getJcaName());
    }

    /**
     * 构建JWT
     *
     * @param duration jwt 有效时间，单位：秒
     * @return JWT字符串
     */
    public static String buildJWT(String uid, long duration) {
        // jwt的签发时间
        long iat = System.currentTimeMillis();
        // jwt的过期时间，这个过期时间必须要大于签发时间
        long exp = iat + duration * 1000;
        // 获取JWT字符串
        String compact = Jwts.builder()
                .signWith(JWT_ALG, SECRET) //加密方式
                .setSubject(DEFAULT_SUB) //说明
                .claim("username",uid)
                .setIssuedAt(new Date(iat)) //签发时间
                .setExpiration(new Date(exp)) //过期时间
                .compact();

        // 在JWT字符串前添加"Bearer "字符串，用于加入"Authorization"请求头
        return JWT_SEPARATOR + compact;
    }


    /**
     * 从 token 中获取 JWT 的 payload 部分
     *
     * @param token token
     * @return {@link Claims}
     */
    private static Claims getClaimsFromToken(String token) throws Exception {
        // 移除 JWT 前的前缀字符串
        token = StringUtils.substringAfter(token, JWT_SEPARATOR);
        // 解析 JWT 字符串
        return Jwts.parser()
                .setSigningKey(SECRET)
                .parseClaimsJws(token)
                .getBody();
    }

    /**
     * 判断token是否有效：判断sub是否在一个分组、判断是否过期
     *
     * @param token token
     * @param sub   subject
     * @return {@link Boolean}
     */
    public static Boolean checkJWT(String token, String sub) {
        try {
            Claims claims = getClaimsFromToken(token);
            if (claims == null || !claims.getSubject().equals(sub) ||
                    claims.getExpiration().before(new Date())) return false;
        } catch (ExpiredJwtException e) {
            // 仅仅是token过期异常直接返回false
            return false;
        } catch (Exception e) {
            return false;
        }
        return true;
    }

    /**
     * 判断token是否有效：判断sub是否在一个分组、判断是否过期
     *
     * @param token token
     * @return true or false
     */
    public static Boolean checkJWT(String token) {
        return checkJWT(token, DEFAULT_SUB);
    }

    /**
     * 从 token 中获取 JWT 的 payload 的 id 内容
     *
     * @param token token
     * @return id value
     */
    public static <T> T getJwtID(String token,String value) {
        try {
            Claims claims = getClaimsFromToken(token);
            return (T) claims.get(value);
        } catch (Exception e) {
            return null;
        }
    }


    public static void main(String[] args) {
        String token =JwtUtils.buildJWT("nuan",3000);

        System.out.println(token);
        System.out.println(checkJWT(token));
        System.out.println((int)getJwtID(token,"exp"));
        System.out.println((String)getJwtID(token,"username"));
    }


}
